// Chromanova!!!

/*
 * Benjamin Hitov
 * Ricky Mondello
 * Brooke Evans
 * Daniel Millstein
 *
 * Game.java - Main game panel. 
 */

package chromafinal;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.*;
import java.awt.event.*;

import java.awt.*;
import java.util.ArrayList;

import java.util.Comparator;
import java.util.Collections;

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.File;

import java.util.Random;

public class Game extends JFrame implements KeyListener{


    Random generator;

    static final Color redp1 = new Color(255, 120, 120);		// red, green, blue
    static final Color greenp1 = new Color(120, 255, 120);

    static final Color redp2 = new Color(200, 10, 10);		// red, green, blue
    static final Color greenp2 = new Color(10, 200, 10);


    private static BufferedImage imageg;

    final int NUM_LEVELS = 10;
    final String LEVEL_BASE = "lset";
    final boolean seq = true;
    final boolean bosses = true;
    final boolean randlevs = true;

    boolean[] levcomp;


    int level;
    int rlevel;

    final int LEVEL_DELAY = 700;
    final int NUM_LIVES = 60;

    public static SoundPlayback fuckingdied1 = new SoundPlayback("chromafinal/sounds/gameover.aif");
    public static SoundPlayback fuckingdied2 = new SoundPlayback("chromafinal/sounds/gameover.aif");
    public static SoundPlayback menu = new SoundPlayback("chromafinal/sounds/menu.aif");
    public static SoundPlayback background_music = new SoundPlayback("chromafinal/sounds/background2.aif");

    Color background_color = new Color(20, 20, 20);

    //how fast key presses will move the players
    private static final int P1SPEED = 1500;
    private static final int P2SPEED = 1500;

    final int MAX_SPEED = 300;

    //how strong the repulse and attract keys will work
    private static final int INTERACTFORCE = 500;

    //self explanatory
    public static final int SCREEN_WIDTH = 700;
    public static final int SCREEN_HEIGHT = 500;

    //roids contains map data
    private ArrayList<LevelObject> roids;
    private int astart, aend;

    private Image dscreen;
    private Graphics dg;

    private Player p1, p2;

    public int lives;

    public static int screenloc;
    int screenlocend;
    private boolean UP_PRESSED, DOWN_PRESSED,LEFT_PRESSED,RIGHT_PRESSED;
    private boolean UP_PRESSED2, DOWN_PRESSED2,LEFT_PRESSED2,RIGHT_PRESSED2;
    private boolean ATTRACT2, REPULSE2;
    private JPanel screen;

    boolean playing;
    boolean lost, won;
    boolean restart;
    boolean loadlevels;

    /*key pressed and keyreleased allow the state of a key to be
     * called as a boolean value even though java only has events
     * built in, not states.
     *
     * P1: arrow keys, L toggles color
     * P2: WASD move, F toggles color
     */
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_UP)
            UP_PRESSED = true;
        else if (e.getKeyCode() == KeyEvent.VK_LEFT)
            LEFT_PRESSED = true;
        else if (e.getKeyCode() == KeyEvent.VK_DOWN)
            DOWN_PRESSED = true;
        else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            RIGHT_PRESSED = true;
        else if (e.getKeyCode() == KeyEvent.VK_W)
            UP_PRESSED2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_A)
            LEFT_PRESSED2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_S)
            DOWN_PRESSED2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_D)
            RIGHT_PRESSED2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_Q)
            ATTRACT2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_E)
            REPULSE2 = true;
        else if (e.getKeyCode() == KeyEvent.VK_F)
            p2.cToggle();
        else if (e.getKeyCode() == KeyEvent.VK_L)
            p1.cToggle();
        else if (e.getKeyCode() == KeyEvent.VK_ENTER && !playing)
            playing = true;
        else if (e.getKeyCode() == KeyEvent.VK_ENTER && !restart && (lost || won))
            restart = true;



    }

    public void keyReleased(KeyEvent e) {
	if (e.getKeyCode() == KeyEvent.VK_UP)
            UP_PRESSED = false;
        else if (e.getKeyCode() == KeyEvent.VK_LEFT)
            LEFT_PRESSED = false;
        else if (e.getKeyCode() == KeyEvent.VK_DOWN)
            DOWN_PRESSED = false;
        else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            RIGHT_PRESSED = false;
        else if (e.getKeyCode() == KeyEvent.VK_W)
            UP_PRESSED2 = false;
        else if (e.getKeyCode() == KeyEvent.VK_A)
            LEFT_PRESSED2 = false;
        else if (e.getKeyCode() == KeyEvent.VK_S)
            DOWN_PRESSED2 = false;
        else if (e.getKeyCode() == KeyEvent.VK_D)
            RIGHT_PRESSED2 = false;
        else if (e.getKeyCode() == KeyEvent.VK_Q)
            ATTRACT2 = false;
        else if (e.getKeyCode() == KeyEvent.VK_E)
            REPULSE2 = false;
    }


    public void keyTyped(KeyEvent e) {}

    //basic constructor
    public Game()
    {
        super("Chromonova");
        initialize();
    }

    //Restarts the game
    public void initialize()
    {
        generator = new Random();
        background_music.stop();
        lives = NUM_LIVES;
        Container c = getContentPane();
        screen = new JPanel();
        c.add(screen, "Center");
        screen.addKeyListener( this);
        screen.setBackground( background_color );
        screen.setPreferredSize( new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        screen.setFocusable(true);
        screen.setVisible(true);
        addKeyListener(this);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        p1 = new Player(100,200,1,redp1,greenp1);
        p2 = new Player(100,300,2,redp2,greenp2);

        screenloc = 0;
        astart = 0;
        aend = 0;
        level = 0;

        levcomp = new boolean[NUM_LEVELS+1];
        
        for (int i = 0; i <+ 10; i++)
            levcomp[i] = false;

        loadNextLevel();

        setSize(SCREEN_WIDTH,SCREEN_HEIGHT);
	setResizable(false);
	setVisible(true);
	setUpGraphics();
        playing = false;
        //lost = false;
        won = false;
        restart = false;
        loadlevels = true;

        menuScreen();
    }

    public boolean lostGame()
    {
        lives = Math.max(NUM_LIVES - (p1.deaths + p2.deaths),0);
        if (lives == 0)
        {
            lost = true;
            return true;
        }
        return false;
        //return (lives > 0);
    }

    //Decrements lives, sets game mode to 'lost' if you are out of lives.
    public void lostALife()
    {
        lives--;
        if (lives <= 0)
            lost = true;

    }

    //Loads next level. If all levels are completed, set game mode to 'won'
    public void loadNextLevel()
    {
        if (level > 0)
            levcomp[rlevel] = true;

        if (level < NUM_LEVELS)
        {
            if (bosses)
            {
                if (level > 0 && level % 3 == 0)
                    bossMode();
            }
            if (level > 0)
            {
            int rnd = generator.nextInt(NUM_LEVELS-level) + 1;
            while (levcomp[rnd])
            {
                rnd--;
                if (rnd < 1)
                    rnd = NUM_LEVELS;
            }
            rlevel = rnd;
            level++;
            }
            else
            {
                rlevel = 1;
                level++;
            }
            //rlevel = generator.nextInt(NUM_LEVELS);

            loadLevel(LEVEL_BASE + rlevel + ".txt");
        }
        else
        {
            if (bosses)
                bossMode();
            loadlevels = false;
            if (!lost)
            {
                won = true;
                gameWon();
            }
        }
    }

    public void bossMode()
    {
        Boss boss = new Boss(400,200,100,70,1,p1,p2,Color.orange);
        int period = 20; //tmp

        while(boss.alive)
        {

            dg.setColor(Color.black);
            dg.fillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
            inputMovement(period);

            //Attraction and repulsion
            new Ball(p1, screenloc, INTERACTFORCE * p1.cnum).pushPlayer(p2);
            new Ball(p2, screenloc, INTERACTFORCE * p2.cnum).pushPlayer(p1);
            boss.push();

            p1.move(period);
            p2.move(period);
            boss.move(period);

            p1.draw(dg);
            p2.draw(dg);
            boss.draw(dg);


            playercollision();
            drawText();

            paintToScreen();
            
            try {
            Thread.sleep(period);
            } catch (InterruptedException ex) {
                Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
    }

    //Level loading helper function
    public void loadLevel(String levname)
    {
        roids = BlankArea.load(levname,LEVEL_DELAY);
        Comparator byxPosComparator = new xPosComparator();
        Collections.sort(roids,byxPosComparator);
        screenlocend = (int) roids.get(roids.size()-1).rightx;
                //+ roids.get(roids.size()-1).size/2;

        roids.add(new Ball(4999+LEVEL_DELAY,1,1));
        astart=0;
        aend=0;
        screenloc=0;
    }

   

    /* Initializes dscreen so that its graphics can be pulled
     */
    public void setUpGraphics()
    {
	dscreen = screen.createImage(SCREEN_WIDTH, SCREEN_HEIGHT);
	dg = dscreen.getGraphics();
    }

    // Computes actions from now until now + period (milliseconds)


    //Play the game until the game ends and a restart is called.
    public void play()
    {
        menu.stop();
        background_music.start();
        
        while(!restart)
        {
            run(20);
        }
        initialize();
    }


    //Perform one step 'period' milliseconds long
    public void run(int period)
    {

        //Smooth transfer to next level after this one is completed
        if(screenloc > screenlocend && loadlevels)
        {
            loadNextLevel();
        }

        //clears the screen by painting it black
        dg.setColor(Color.black);
        dg.fillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);

        dg.setColor(Color.red);

        //These loops make sure only asteroids on the screen are drawn
        while(astart < roids.size() && ((roids.get(astart)).passedScreen()))
            astart++;
        while(aend < roids.size() - 1 && (roids.get(aend)).onScreen())
            aend++;

        LevelObject roid;

       //computes effect of all asteroids on screen
        for (int i = astart; i < aend; i++)
        {
            roid = (LevelObject) roids.get(i);
            roid.draw(dg);
            if (!lost)
            {
                if (!p1.invincible())
                    roid.pushPlayer(p1);
                    //    lostALife();
                if (!p2.invincible())
                    roid.pushPlayer(p2);
                    //    lostALife();
            }
        }

        //Only makes player calculations if they have lives left
        if (!lostGame())
        {
            inputMovement(period);

            //Attraction and repulsion
            new Ball(p1, screenloc, INTERACTFORCE * p1.cnum).pushPlayer(p2);
            new Ball(p2, screenloc, INTERACTFORCE * p2.cnum).pushPlayer(p1);

            p1.move(period);
            p2.move(period);

            p1.draw(dg);
            p2.draw(dg);


            playercollision();
            drawText();
        }
        else
            gameLost();

        if(won)
            gameWon();
        paintToScreen();

        //move asteroids to the left
        screenloc += 3;

        try {
            Thread.sleep(period);
        } catch (InterruptedException ex) {
            Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    //Initializes menu screen and waits for enter key press
    public void menuScreen()
    {
        try {
            imageg = ImageIO.read(new File("src/chromafinal/images/Chromanova.jpg"));
        } catch (IOException ex) {
            System.err.println("Failed to load intro image.");
        }

        dg.drawImage(imageg, 0, 0, null);
        Graphics g = screen.getGraphics();
        menu.start();

        while(!playing)
        {
            paintToScreen();

            try {
            Thread.sleep(20);
            } catch (InterruptedException ex) {
            Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        play();
    }

    public static void main(String args[])
    {
        Game gameObjects = new Game();
    }


    //Checks to see if players are touching each other. If they are, kill
    //them both
    public void playercollision()
    {
        double dist = Math.pow(Math.pow(p1.xl-p2.xl,2) + Math.pow(p1.yl-p2.yl,2),.5);
        if (dist < 16)
        {
            if(p1.die())
                lostALife();
            if(p2.die())
                lostALife();
        }
    }

    //Parse input keys
    public void inputMovement(int period)
    {
        double c1speed = Math.pow(p1.xv*p1.xv + p1.yv * p1.yv,0.5);
        double c2speed = Math.pow(p2.xv*p2.xv + p2.yv * p2.yv,0.5);

        double mtime = period/1000.0;

        if (LEFT_PRESSED)
            p1.xv-= P1SPEED * mtime;
        if (RIGHT_PRESSED)
            p1.xv+= P1SPEED * mtime;
        if (DOWN_PRESSED)
            p1.yv+= P1SPEED * mtime;
        if (UP_PRESSED)
            p1.yv-= P1SPEED * mtime;

        if (LEFT_PRESSED2)
            p2.xv-= P2SPEED * mtime;
        if (RIGHT_PRESSED2)
            p2.xv+= P2SPEED * mtime;
        if (DOWN_PRESSED2)
            p2.yv+= P2SPEED * mtime;
        if (UP_PRESSED2)
            p2.yv-= P2SPEED * mtime;

        double e1speed = Math.pow(p1.xv*p1.xv + p1.yv * p1.yv,0.5);
        double e2speed = Math.pow(p2.xv*p2.xv + p2.yv * p2.yv,0.5);
        double sMod;

        if (c1speed > MAX_SPEED && e1speed > c1speed)
        {
            sMod = c1speed / e1speed;
            p1.xv = p1.xv * sMod;
            p1.yv = p1.yv * sMod;
        }
        if (c2speed > MAX_SPEED && e2speed > c2speed)
        {
            sMod = c2speed / e2speed;
            p2.xv = p2.xv * sMod;
            p2.yv = p2.yv * sMod;
        }
    }


    //draws lives and level number to screen
    public void drawText()
    {
        Font font = new Font("Arial", Font.PLAIN, 20);
        dg.setFont(font);
        dg.setColor(Color.white);
        dg.drawString( "Lives: "+lives, 600, 20);
        dg.drawString( "Level: "+level,600,40);
        dg.drawString( "Stage: "+rlevel,600,60);

    }

    //paints prebuffered graphics
    public void paintToScreen()
    {
        Graphics g;
        try {
            g = screen.getGraphics();
            if ((g != null) && (dscreen != null))
            g.drawImage(dscreen, 0, 0, null);
            Toolkit.getDefaultToolkit().sync();
            g.dispose();
        }
        catch (Exception e)
        { System.out.println("Graphics error: " + e);  }

    }

    //Comparison function for the Ball class that allows sorting
    class xPosComparator implements Comparator<LevelObject>
        {
            public int compare(LevelObject balla, LevelObject ballb)
            {
                if(balla.leftx <= ballb.leftx)
                    return -1;
                else
                    return 1;
            }
        }

    //Draws game lost text
    public void gameLost()
    {
        //lost = true;
        Font font = new Font("Arial", Font.BOLD, 100);
        dg.setFont(font);
        dg.setColor(Color.white);
        dg.drawString( "YOU LOSE", 90, 260);
        enterToRestartText();
    }

    //Draws game won text
    public void gameWon()
    {
        Font font = new Font("Arial", Font.BOLD, 100);
        dg.setFont(font);
        dg.setColor(Color.white);
        dg.drawString( "YOU WIN!", 105, 260);
        enterToRestartText();
    }

    //Draws press enter to restart text
    public void enterToRestartText()
    {
        Font font = new Font("Arial", Font.PLAIN, 15);
        dg.setFont(font);
        dg.setColor(Color.white);
        dg.drawString( "Press ENTER to restart.", 265, 300);
    }

}
